// infer类型推断 用于条件类型中，声明一个待推断的类型变量。当条件匹配时，TypeScript 会自动推导出该变量的具体类型。

// infer获取参数与返回值
interface Customer { username: string; buymoney: number }
type CustFn = (param: Customer) => string

// ### 提取某个函数的参数类型
type CustFnParamType = CustFn extends (param: infer P) => any ? P : CustFn
// 提取某个函数的返回值类型
type CustFnReturnType = CustFn extends (param: any) => infer R ? R : CustFn // type CustFnReturnType = string

// ### infer提取通用的函数参数的类型
type ReturnType<T> = T extends (param: any) => infer R ? R : never
type CustFnReturnType2 = ReturnType<CustFn>  // type CustFnReturnType2 = string

// ### infer提取通用的泛型数组中，元素的类型。
type EleTypeOfArr<T> = T extends Array<infer P> ? P : never
type EleTypeOfArray = EleTypeOfArr<Array<string>>  // type EleTypeOfArray = string

//  ### vue3源码对infer的应用。
// 当unref中传入ref方法时，获取ref方法里的参数。当传入的是非ref对象时，获取这个参数
interface Ref<T = any> { value: T; }
function ref<T>(param: T): Ref<T> { return { value: param } }
function isRef(ref: any) { return true }

function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
  return isRef(ref) ? (ref as any).value : ref as any
}
unref(ref(3))

// ### 作业题，allSubject的类型是Set<Subject>，用infer，将已经具体化的Subject泛型拿出来
class Subject { constructor(public subid: number, public subname: string) {} }
let chineseSubject = new Subject(100, "语文")
let mathSubject = new Subject(100, "数学")
let allSubject = new Set([chineseSubject, mathSubject])

type GetSetType<T> = T extends Set<infer U> ? U : never
type GetSubjectType = GetSetType<typeof allSubject>  // type GetSubjectType = Subject
export {}
